package me.kevingleason.androidrtc;

import android.os.Handler;
import android.os.SystemClock;

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Created by win7 on 2016-9-21.
 */
public class ThreadUtils {
    public ThreadUtils() {
    }

    public static void executeUninterruptibly(ThreadUtils.BlockingOperation operation) {
        boolean wasInterrupted = false;

        while(true) {
            try {
                operation.run();
                break;
            } catch (InterruptedException var3) {
                wasInterrupted = true;
            }
        }

        if(wasInterrupted) {
            Thread.currentThread().interrupt();
        }

    }

    public static boolean joinUninterruptibly(Thread thread, long timeoutMs) {
        long startTimeMs = SystemClock.elapsedRealtime();
        long timeRemainingMs = timeoutMs;
        boolean wasInterrupted = false;

        while(timeRemainingMs > 0L) {
            try {
                thread.join(timeRemainingMs);
                break;
            } catch (InterruptedException var11) {
                wasInterrupted = true;
                long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
                timeRemainingMs = timeoutMs - elapsedTimeMs;
            }
        }

        if(wasInterrupted) {
            Thread.currentThread().interrupt();
        }

        return !thread.isAlive();
    }

    public static void joinUninterruptibly(final Thread thread) {
        executeUninterruptibly(new ThreadUtils.BlockingOperation() {
            public void run() throws InterruptedException {
                thread.join();
            }
        });
    }

    public static void awaitUninterruptibly(final CountDownLatch latch) {
        executeUninterruptibly(new ThreadUtils.BlockingOperation() {
            public void run() throws InterruptedException {
                latch.await();
            }
        });
    }

    public static boolean awaitUninterruptibly(CountDownLatch barrier, long timeoutMs) {
        long startTimeMs = SystemClock.elapsedRealtime();
        long timeRemainingMs = timeoutMs;
        boolean wasInterrupted = false;
        boolean result = false;

        while(true) {
            try {
                result = barrier.await(timeRemainingMs, TimeUnit.MILLISECONDS);
                break;
            } catch (InterruptedException var12) {
                wasInterrupted = true;
                long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
                timeRemainingMs = timeoutMs - elapsedTimeMs;
                if(timeRemainingMs <= 0L) {
                    break;
                }
            }
        }

        if(wasInterrupted) {
            Thread.currentThread().interrupt();
        }

        return result;
    }

    public static <V> V invokeAtFrontUninterruptibly(Handler handler, final Callable<V> callable) {
        if(handler.getLooper().getThread() == Thread.currentThread()) {
            try {
                Object result1 = callable.call();
                return (V) result1;
            } catch (Exception var5) {
                RuntimeException runtimeException = new RuntimeException("Callable threw exception: " + var5);
                runtimeException.setStackTrace(var5.getStackTrace());
                throw runtimeException;
            }
        } else {
            class Result {
                public V value;

                Result() {
                }
            }

            final Result result = new Result();
            final CountDownLatch barrier = new CountDownLatch(1);
            handler.post(new Runnable() {
                public void run() {
                    try {
                        result.value = callable.call();
                    } catch (Exception var3) {
                        RuntimeException runtimeException = new RuntimeException("Callable threw exception: " + var3);
                        runtimeException.setStackTrace(var3.getStackTrace());
                        throw runtimeException;
                    }

                    barrier.countDown();
                }
            });
            awaitUninterruptibly(barrier);
            return result.value;
        }
    }

    public static void invokeAtFrontUninterruptibly(Handler handler, final Runnable runner) {
        if(handler.getLooper().getThread() == Thread.currentThread()) {
            runner.run();
        } else {
            final CountDownLatch barrier = new CountDownLatch(1);
            handler.postAtFrontOfQueue(new Runnable() {
                public void run() {
                    runner.run();
                    barrier.countDown();
                }
            });
            awaitUninterruptibly(barrier);
        }
    }

    public interface BlockingOperation {
        void run() throws InterruptedException;
    }

    public static class ThreadChecker {
        private Thread thread = Thread.currentThread();

        public ThreadChecker() {
        }

        public void checkIsOnValidThread() {
            if(this.thread == null) {
                this.thread = Thread.currentThread();
            }

            if(Thread.currentThread() != this.thread) {
                throw new IllegalStateException("Wrong thread");
            }
        }

        public void detachThread() {
            this.thread = null;
        }
    }
}
